/**
 * @class   wosh::Variant
 * @brief   A generic data holder. Provides also conversions and ASCII serialization.
 *
 ****************************************************************************
 * @version 0.8.462 $Id: Variant.h 2863 2010-08-07 04:28:58Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_Variant_H__
 #define __WOSH_Core_Variant_H__

 #include <woshDefs.h>
 #include <core/Serialization.h>

namespace wosh {

class Variant : public ISerializable
 {
	WOSH_SERIALIZABLE(wosh::Variant)
	WOSH_COMPAREX_RTTI(wosh::Variant)

	public:
		/// types of the data
		enum DATA_TYPE {
			EMPTY		= 0x00, // memset(0), unknown = empty
			BOOLEAN		= 0x01, // as bool
			CHAR		= 0x02, // as char
			INTEGER		= 0x03, // as long
			LONG		= 0x04, // as long
			INT64		= 0x05, // as long long
			DATETIME	= 0x06, // as long long
			TIMESPAN	= 0x07, // as long long
			DOUBLE		= 0x08, // as double
			FLOAT		= 0x09, // as float

			CHARS		= 0x10, // 16
			STRING		= 0x11
		 };
		// TODO?: enum type (with name info), bitset

		// pure C struct, about 8 bytes?
		 union tVariantUnion {
			bool v_bool;
			char v_char;
			long v_long;
			int64 v_int64;
			double v_double;
			float v_float;

			char* v_pchar;
			std::string* v_pstr;
		 };


	public:
		Variant( )
			: vType(EMPTY) {
			memset(&this->vHolder, 0, sizeof(vHolder));
		 }
		Variant( DATA_TYPE type )
			: vType(type) {
			memset(&this->vHolder, 0, sizeof(vHolder));
		 }

		Variant( const bool& value )
			: vType(BOOLEAN) {
			this->vHolder.v_bool = value;
		 }
		Variant( const int& value )
			: vType(INTEGER) {
			this->vHolder.v_long = (long)value; // upcasted
		 }
		Variant( const unsigned int& value )
			: vType(INTEGER) {
			this->vHolder.v_long = (long)value; // upcasted
		 }
		Variant( const long& value )
			: vType(LONG) {
			this->vHolder.v_long = value;
		 }
		Variant( const unsigned long& value )
			: vType(INT64) {
			this->vHolder.v_int64 = value; // upcasted
		 }
		Variant( const int64& value )
			: vType(INT64) {
			this->vHolder.v_int64 = value;
		 }
		Variant( const double& value )
			: vType(DOUBLE) {
			this->vHolder.v_double = value;
		 }
		Variant( const float& value )
			: vType(FLOAT) {
			this->vHolder.v_float = value;
		 }
		Variant( const std::string& value )
			: vType(STRING) {
			this->vHolder.v_pstr = new std::string(value);
		 }
		Variant( const char* value )
			: vType(STRING) {
			this->vHolder.v_pstr = new std::string(value);
		 }

		// 'explicit' constructos for implicit conversion a template would makes
		// things harder to debug, for example you could acquire pointers as booleans..

		template <typename T>
		explicit Variant( const T& value, DATA_TYPE forceType )
			: vType(EMPTY) {
			set( (const T&)value, forceType);
		 }
		/**
		 * @brief  Copy constructor.
		 * @param m [in] the source object to be copied
		 */
		Variant( const Variant& m )
			: vType(EMPTY) {
			*this = m;
		 }

		explicit Variant( const ISerializable* ) : vType(EMPTY) { }
		explicit Variant( ISerializable* ) : vType(EMPTY) { }

	public:
		/**
		 * @brief  Destructor, eventually free internal buffers.
		 */
		virtual ~Variant() {
			freePtrs();
		 }


/** @name Setters
 * @{
 ******************************************************************************/
	public:
		inline void set( const bool& value ) {
			freePtrs();
			this->vType = BOOLEAN;
			this->vHolder.v_bool = value;
		 }
		inline void set( const char& value ) {
			freePtrs();
			this->vType = CHAR;
			this->vHolder.v_char = value;
		 }
		inline void set( const unsigned char& value ) {
			freePtrs();
			this->vType = CHAR;
			this->vHolder.v_char = value;
		 }
		inline void set( const int& value ) {
			freePtrs();
			this->vType = INTEGER;
			this->vHolder.v_long = value;
		 }
		inline void set( const unsigned int& value ) {
			freePtrs();
			this->vType = INTEGER;
			this->vHolder.v_long = value; // upcasted
		 }
		// because often we have date in long-type (epoch)
		inline void set( const long& value, DATA_TYPE dataType = LONG ) {
			freePtrs();
			if ( dataType < INTEGER || dataType > TIMESPAN ) {
				// force default, set aligned
				this->vType = LONG;
				this->vHolder.v_long = value;
			 }
			else {  // casting
				this->vType = dataType;
				this->vHolder.v_int64 = (int64)value;
			 }
		 }
		// because often we have date in long-type (epoch)
		inline void set( const unsigned long& value, DATA_TYPE dataType = INT64 ) {
			freePtrs();
			if ( dataType == LONG ) {
				this->vType = LONG;
				this->vHolder.v_long = (long)value;
			 }
			else if ( dataType < INT64 || dataType > TIMESPAN ) {
				// force default, set aligned
				this->vType = INT64;
				this->vHolder.v_int64 = (int64)value; // upcasted
			 }
			else {  // casting
				this->vType = dataType;
				this->vHolder.v_int64 = (int64)value; // upcasted
			 }
		 }
		// because also time-types share same memory type:
		inline void set( const int64& value, DATA_TYPE dataType = INT64 ) {
			freePtrs();
			this->vHolder.v_int64 = value;
			if ( dataType < INT64 || dataType > TIMESPAN )
				this->vType = INT64;
			else
				this->vType = dataType;
		 }
		inline void set( const double& value ) {
			freePtrs();
			this->vType = DOUBLE;
			this->vHolder.v_double = value;
		 }
		inline void set( const float& value ) {
			freePtrs();
			this->vType = FLOAT;
			this->vHolder.v_float = value;
		 }
		// create NEW or string assignement-operator
		inline void set( const std::string& value ) {
			if ( this->vType == STRING && this->vHolder.v_pstr != NULL )
				*this->vHolder.v_pstr = value;
			else {
				freePtrs();
				this->vType = STRING;
				this->vHolder.v_pstr = new std::string(value);
			 }
		 }
		// must be NULL terminated, considered as STRING!!!!
		inline void set( const char* value ) {
			if ( this->vType == STRING && this->vHolder.v_pstr != NULL )
				this->vHolder.v_pstr->assign(value);
			else {
				freePtrs();
				this->vType = STRING;
				this->vHolder.v_pstr = new std::string(value);
			 }
		 }
	private:
		 inline void set( const ISerializable* ) { }

	public:
		DATA_TYPE fromString( const std::string& data, DATA_TYPE type_tip = EMPTY );

		void clear() {
			freePtrs();
			memset(&this->vHolder, 0, sizeof(vHolder));
			this->vType = EMPTY;
		 }

		void freePtrs() {
			if ( this->vType == STRING && this->vHolder.v_pstr != NULL ) {
				delete this->vHolder.v_pstr; this->vHolder.v_pstr = NULL; }
			else if ( this->vType == CHARS && this->vHolder.v_pchar != NULL ) {
				delete [] this->vHolder.v_pchar; this->vHolder.v_pchar = NULL; }
		 }

//@}
/** @name Getters
 * @{
 ******************************************************************************/
	public:
		DATA_TYPE getType() const								{ return this->vType; }

		inline bool isEmpty() const								{ return (this->vType == EMPTY); }
		inline bool isBoolean() const							{ return (this->vType == BOOLEAN); }
		inline bool isChar() const								{ return (this->vType == CHAR); }
		inline bool isInteger() const							{ return (this->vType == INTEGER); }
		inline bool isLong() const								{ return (this->vType == LONG); }
		inline bool isInt64() const								{ return (this->vType == INT64); }
		inline bool isDateTime() const							{ return (this->vType == DATETIME); }
		inline bool isTimeSpan() const							{ return (this->vType == TIMESPAN); }
		inline bool isDouble() const							{ return (this->vType == DOUBLE); }
		inline bool isFloat() const								{ return (this->vType == FLOAT); }
		inline bool isCharBuffer() const						{ return (this->vType == CHARS); }

		inline bool isString( bool check_valid = true ) const {
			if (this->vType != STRING ) return false;
			if ( check_valid && this->vHolder.v_pstr == NULL ) return false;
			return true;
		 }
		// return false only is is a non-empty string
		inline bool isStringNotEmpty() const {
			if ( !isString(true) ) return false;
			return !this->vHolder.v_pstr->empty();
		 }
		inline bool isNumeric() const							{ return (this->vType >= INTEGER && this->vType <= FLOAT); }

		// NOT type-check SAFE 
		inline bool asBoolean() const							{ return this->vHolder.v_bool; }
		inline char asChar() const								{ return this->vHolder.v_char; }
		inline int asInteger() const							{ return this->vHolder.v_long; }
		inline long asLong() const								{ return this->vHolder.v_long; }
		inline int64 asInt64() const							{ return this->vHolder.v_int64; }
		inline int64 asDateTime() const							{ return this->vHolder.v_int64; }
		inline int64 asTimeSpan() const							{ return this->vHolder.v_int64; }
		inline double asDouble() const							{ return this->vHolder.v_double; }
		inline float asFloat() const							{ return this->vHolder.v_float; }

		template <typename T>
		inline T as( const T& default_value ) const				{ return default_value; }
		inline bool as( const bool& default_value ) const		{ if (isBoolean()) return asBoolean(); return default_value; }
		inline int as( const int& default_value ) const			{ if (isInteger()) return asInteger(); return default_value; }
		inline long as( const long& default_value ) const		{ if (isLong()) return asLong(); return default_value; }
		inline int64 as( const int64& default_value ) const		{ if (isInt64()) return asInt64(); return default_value; }
		inline double as( const double& default_value ) const	{ if (isDouble()) return asDouble(); return default_value; }
		inline float as( const float& default_value ) const		{ if (isFloat()) return asFloat(); return default_value; }

		// should be NULL terminated
		inline char* asCharPtr() const							{ return this->vHolder.v_pchar; }

		inline const std::string* asStringPtr() const			{ return this->vHolder.v_pstr; }
		// not safe, should previously check calling isString(true)
		inline const std::string& asString() const				{ return *this->vHolder.v_pstr; }
		inline std::string& asString()							{ return *this->vHolder.v_pstr; }

		// SAFE + CONVERSION (when required)
		bool toBoolean( bool default_value ) const;
		int toInteger( int default_value ) const;
		long toLong( long default_value ) const;
		int64 toInt64( int64 default_value ) const;
		double toDouble( double default_value ) const;
		float toFloat( float default_value ) const;
		// toString() follows ISerializable interface !!

		template <typename T>
		inline T to( const T& default_value ) const				{ return default_value; }
		inline bool to( const bool& default_value ) const		{ return toBoolean(default_value); }
		inline int to( const int& default_value ) const			{ return toInteger(default_value); }
		inline long to( const long& default_value ) const		{ return toLong(default_value); }
		inline int64 to( const int64& default_value ) const		{ return toInt64(default_value); }
		inline double to( const double& default_value ) const	{ return toDouble(default_value); }
		inline float to( const float& default_value ) const		{ return toFloat(default_value); }

		inline const char* getTypeAsString( bool short_name = false ) const {
			return getTypeAsString(this->vType, short_name);
		 }

		static DATA_TYPE getTypeFromString( const char* type, short short_name = 0 );

		static const char* getTypeAsString( DATA_TYPE type, bool short_name = false );


//@}
/** @name Operators Overloading
 * @{
 ******************************************************************************/
	public:
		template <typename T>
		inline Variant& operator=( const T& m ) {
			if (this == &m) return *this; // same object?
			set(m);
			return *this;
		 }

		Variant& operator=( const Variant& m );
		inline Variant& operator=( const bool& m )				{ set(m); return *this; }
		inline Variant& operator=( const char& m )				{ set(m); return *this; }
		inline Variant& operator=( const int& m )				{ set(m); return *this; }
		inline Variant& operator=( const long& m )				{ set(m); return *this; }
		inline Variant& operator=( const int64& m )				{ set(m); return *this; }
		inline Variant& operator=( const double& m )			{ set(m); return *this; }
		inline Variant& operator=( const float& m )				{ set(m); return *this; }
		inline Variant& operator=( const std::string& m )		{ set(m); return *this; }

		bool operator==( const Variant& other ) const;
		bool operator==( const bool& other ) const;
		inline bool operator==( const char& other ) const			{ if (!isChar()) return false; return (asChar()==other); }
		bool operator==( const int& other ) const;
		bool operator==( const long& other ) const;
		bool operator==( const int64& other ) const;
		inline bool operator==( const double& other ) const			{ if (!isDouble()) return false; return (asDouble()==other); }
		inline bool operator==( const float& other ) const			{ if (!isFloat()) return false; return (asFloat()==other); }
		bool operator==( const std::string& other ) const;
		bool operator==( const char* other ) const; // considered as string

		inline bool operator!=( const Variant& other ) const		{ return !(*this == other);  }
		inline bool operator!=( const bool& other ) const			{ return !(*this == other);  }
		inline bool operator!=( const char& other ) const			{ return !(*this == other);  }
		inline bool operator!=( const int& other ) const			{ return !(*this == other);  }
		inline bool operator!=( const long& other ) const			{ return !(*this == other);  }
		inline bool operator!=( const int64& other ) const			{ return !(*this == other);  }
		inline bool operator!=( const double& other ) const			{ return !(*this == other);  }
		inline bool operator!=( const float& other ) const			{ return !(*this == other);  }
		inline bool operator!=( const std::string& other ) const	{ return !(*this == other);  }
		inline bool operator!=( const char* other ) const			{ return !(*this == other);  }

		const Variant operator+( const Variant& other ) const;

		bool operator>( const Variant& other ) const;
		bool operator<( const Variant& other ) const;

//@}
/** @name Serialization interface
 * @{
 ******************************************************************************/
	public:
		virtual std::string toString() const;
		virtual bool updateHash( DataHashContext* hash_context ) const;
//@}

	protected:
		DATA_TYPE vType;
		tVariantUnion vHolder;

	public:
		static const Variant EmptyVariant;

}; // class def


class Variant_DT : public Variant
 {
	public:
		Variant_DT()
			: Variant(DATETIME) { }
		Variant_DT( const int& value )
			: Variant(DATETIME) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_DT( const unsigned int& value )
			: Variant(DATETIME) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_DT( const long& value )
			: Variant(DATETIME) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_DT( const unsigned long& value )
			: Variant(DATETIME) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_DT( const int64& value )
			: Variant(DATETIME) {
			this->vHolder.v_int64 = value;
		 }
};

class Variant_TS : public Variant
 {
	public:
		Variant_TS()
			: Variant(TIMESPAN) { }
		Variant_TS( const int& value )
			: Variant(TIMESPAN) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_TS( const unsigned int& value )
			: Variant(TIMESPAN) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_TS( const long& value )
			: Variant(TIMESPAN) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_TS( const unsigned long& value )
			: Variant(TIMESPAN) {
			this->vHolder.v_int64 = (int64)value; // upcasted
		 }
		Variant_TS( const int64& value )
			: Variant(TIMESPAN) {
			this->vHolder.v_int64 = value;
		 }
};


}; // namespace wosh

#endif //__WOSH_Core_Variant_H__
